<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

function parseAmpersAnd( $html )
{
    $entities = array( 'quot', 'amp', 'apos', 'lt', 'gt', 'nbsp', 'iexcl', 'cent',
        'pound', 'curren', 'yen'
        , 'brvbar', 'sect', 'uml', 'copy', 'ordf', 'laquo', 'not', 'shy', 'reg',
        'macr', 'deg'
        , 'plusmn', 'sup2', 'sup3', 'acute', 'micro', 'para', 'middot', 'cedil',
        'sup1', 'ordm'
        , 'raquo', 'frac14', 'frac12', 'frac34', 'iquest', 'Agrave', 'Aacute', 'Acirc',
        'Atilde'
        , 'Auml', 'Aring', 'AElig', 'Ccedil', 'Egrave', 'Eacute', 'Ecirc', 'Euml',
        'Igrave'
        , 'Iacute', 'Icirc', 'Iuml', 'ETH', 'Ntilde', 'Ograve', 'Oacute', 'Ocirc',
        'Otilde', 'Ouml'
        , 'times', 'Oslash', 'Ugrave', 'Uacute', 'Ucirc', 'Uuml', 'Yacute', 'THORN',
        'szlig'
        , 'agrave', 'aacute', 'acirc', 'atilde', 'auml', 'aring', 'aelig', 'ccedil',
        'egrave'
        , 'eacute', 'ecirc', 'euml', 'igrave', 'iacute', 'icirc', 'iuml', 'eth',
        'ntilde', 'ograve'
        , 'oacute', 'ocirc', 'otilde', 'ouml', 'divide', 'oslash', 'ugrave', 'uacute',
        'ucirc'
        , 'uuml', 'yacute', 'thorn', 'yuml', 'OElig', 'oelig', 'Scaron', 'scaron',
        'Yuml', 'fnof'
        , 'circ', 'tilde', 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta',
        'Eta', 'Theta'
        , 'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma',
        'Tau'
        , 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega', 'alpha', 'beta', 'gamma', 'delta',
        'epsilon'
        , 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron',
        'pi', 'rho'
        , 'sigmaf', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega', 'thetasym',
        'upsih'
        , 'piv', 'ensp', 'emsp', 'thinsp', 'zwnj', 'zwj', 'lrm', 'rlm', 'ndash',
        'mdash', 'lsquo'
        , 'rsquo', 'sbquo', 'ldquo', 'rdquo', 'bdquo', 'dagger', 'Dagger', 'bull',
        'hellip'
        , 'permil', 'prime', 'Prime', 'lsaquo', 'rsaquo', 'oline', 'frasl', 'euro',
        'image', 'weierp'
        , 'real', 'trade', 'alefsym', 'larr', 'uarr', 'rarr', 'darr', 'harr', 'crarr',
        'lArr', 'uArr'
        , 'rArr', 'dArr', 'hArr', 'forall', 'part', 'exist', 'empty', 'nabla', 'isin',
        'notin', 'ni'
        , 'prod', 'sum', 'minus', 'lowast', 'radic', 'prop', 'infin', 'ang', 'and',
        'or', 'cap', 'cup'
        , 'int', 'there4', 'sim', 'cong', 'asymp', 'ne', 'equiv', 'le', 'ge', 'sub',
        'sup', 'nsub'
        , 'sube', 'supe', 'oplus', 'otimes', 'perp', 'sdot', 'lceil', 'rceil', 'lfloor',
        'rfloor'
        , 'lang', 'rang', 'loz', 'spades', 'clubs', 'hearts', 'diams' );

    $pilaTrozos = array( ); //para cada contenido que extraiga parte se tiene que poner el primer trozo y parte en el segundo
    $trozosHTML = array( '', $html );
    $pos_ini = 0;

    while ( $pos_ini < strlen( $trozosHTML[ 1 ] ) && ( $pos_ini = strpos( $trozosHTML[ 1 ],
    '&', $pos_ini ) ) !== false ) {


        if ( ( $pos_fin = strpos( $trozosHTML[ 1 ], ';', $pos_ini + 1 ) ) !== false ) {

            $interior = substr( $trozosHTML[ 1 ], $pos_ini + 1,
                    $pos_fin - $pos_ini - 1 );

            if ( in_array( $interior, $entities ) || preg_match( '/^#[0-9]{2,4}$/',
                            $interior ) || preg_match( '/^#x[0-9a-f]{2,4}$/',
                            strtolower( $interior ) ) ) {

                $pilaTrozos[ ] = array( '', htmlspecialchars( substr( $trozosHTML[ 1 ],
                                    0, $pos_ini ) ) . '&' . $interior . ';' );

                $trozosHTML[ 1 ] = substr( $trozosHTML[ 1 ], $pos_fin + 1 );
                $pos_ini = 0;
            }
            else $pos_ini++;
        }
        else break;
    }

    //  $trozosHTML[0] = htmlspecialchars( $trozosHTML[0] );
    $trozosHTML[ 1 ] = htmlspecialchars( $trozosHTML[ 1 ] );

    while ( $trozos = array_pop( $pilaTrozos ) ) {
        //    $trozosHTML[0]=$trozosHTML[0].$trozos[0];
        $trozosHTML[ 1 ] = $trozos[ 1 ] . $trozosHTML[ 1 ];
    }

    return $trozosHTML[ 0 ] . $trozosHTML[ 1 ];
}

function parseAndEraseJavascript( $string )
{
    $internoValidado = '';

    $coincidencias = array( );
    if ( preg_match_all( '/([a-z]+)\s*=\s*"([^"]+)"/', $string, $coincidencias ) ) { //todo lo que no pase la expresion regular lo quitamos
        $num = count( $coincidencias[ 0 ] );

        for ( $i = 0; $i < $num; $i++ )
                if ( substr( $coincidencias[ 1 ][ $i ], 0, 2 ) != 'on' && substr( ltrim( $coincidencias[ 2 ][ $i ] ),
                            0, 10 ) != 'javascript' )
                    $internoValidado.=' ' . $coincidencias[ 1 ][ $i ] . '="' . $coincidencias[ 2 ][ $i ] . '"';
    }

    $coincidencias = array( );
    if ( preg_match_all( '/([a-z]+)\s*=\s*\'([^\']+)\'/', $string,
                    $coincidencias ) ) { //todo lo que no pase la expresion regular lo quitamos
        $num = count( $coincidencias[ 0 ] );

        for ( $i = 0; $i < $num; $i++ )
                if ( substr( $coincidencias[ 1 ][ $i ], 0, 2 ) != 'on' && substr( ltrim( $coincidencias[ 2 ][ $i ] ),
                            0, 10 ) != 'javascript' )
                    $internoValidado.=' ' . $coincidencias[ 1 ][ $i ] . '=\'' . $coincidencias[ 2 ][ $i ] . '\'';
    }

    return $internoValidado;
}

function parseAmpersAndSelfClosed( $html, $tags )
{
    $pilaTrozos = array( ); //para cada contenido que extraiga parte se tiene que poner el primer trozo y parte en el segundo
    $trozosHTML = array( '', $html );
    $pos_fin = 0;

    while ( $pos_fin < strlen( $trozosHTML[ 1 ] ) && ( $pos_fin = strpos( $trozosHTML[ 1 ],
    '/>', $pos_fin ) ) !== false ) {

        $pos_tmp = 0;
        $pos_ini = false;

        while ( ( $pos_tmp = strpos( $trozosHTML[ 1 ], '<', $pos_tmp + 1 ) ) !== false && $pos_tmp < $pos_fin )
            $pos_ini = $pos_tmp;

        if ( $pos_ini !== false ) {

            $pos_mid = strpos( $trozosHTML[ 1 ], ' ', $pos_ini );

            if ( $pos_mid === false ) $pos_mid = $pos_fin;

            $etiqueta = rtrim( substr( $trozosHTML[ 1 ], $pos_ini + 1,
                            $pos_mid - $pos_ini - 1 ) );

            if ( in_array( $etiqueta, $tags ) ) {

                if ( $pos_mid != $pos_fin )
                        $contenidoActual = '<' . $etiqueta . ' ' . parseAndEraseJavascript( substr( $trozosHTML[ 1 ],
                                            $pos_mid, $pos_fin - $pos_mid - 1 ) ) . ' />';
                else $contenidoActual = '<' . $etiqueta . ' />';


                $pilaTrozos[ ] = array( '', parseAmpersAnd( substr( $trozosHTML[ 1 ],
                                    0, $pos_ini ) ) . $contenidoActual );

                $trozosHTML[ 1 ] = substr( $trozosHTML[ 1 ], $pos_fin + 2 );
                $pos_fin = 0;
            }
            else $pos_fin+=2;
        }
        else break;
    }

    //  $trozosHTML[0] = htmlspecialchars( $trozosHTML[0] );
    $trozosHTML[ 1 ] = parseAmpersAnd( $trozosHTML[ 1 ] );

    while ( $trozos = array_pop( $pilaTrozos ) ) {
        //    $trozosHTML[0]=$trozosHTML[0].$trozos[0];
        $trozosHTML[ 1 ] = $trozos[ 1 ] . $trozosHTML[ 1 ];
    }

    return $trozosHTML[ 0 ] . $trozosHTML[ 1 ];
}

function findStartTag( $etiqueta, $cadena, $pos_fin_busqueda = null )
{

    if ( $pos_fin_busqueda === null ) $pos_fin_busqueda = strlen( $cadena ) - 1;

    $longitud = strlen( $etiqueta );

    //Ahora tengo que buscar la etiqueta de inicio o en la cadena fin o en el cadena inicio
    // $pos_fin_busqueda = $pos_ini_etiq_fin;

    while ( $pos_fin_busqueda !== false ) {

        $pos_ini_etiq_ini = false;

        //echo 'Buscando apertura pos_fin at '.$pos_fin.'<br />'."\r\n";
        $pos_tmp = $longitud * -1;

        while ( ( $pos_tmp = strpos( $cadena, '<' . $etiqueta,
        $pos_tmp + $longitud ) ) !== false && $pos_tmp < $pos_fin_busqueda )
            $pos_ini_etiq_ini = $pos_tmp;

        $pos_fin_busqueda = $pos_ini_etiq_ini;

        if ( $pos_ini_etiq_ini !== false ) {

            $pos_mid_etiq_ini = $pos_ini_etiq_ini + $longitud + 1;

            $pos_fin_etiq_ini = strpos( $cadena, '>', $pos_mid_etiq_ini );

            //?puedo asegurar q es esta?: dentro de una etiqueta no debería haber
            if ( $pos_fin_etiq_ini !== false && substr( $cadena,
                            $pos_fin_etiq_ini - 1, 1 ) != '/' )
                    return array( $pos_ini_etiq_ini, $pos_fin_etiq_ini );
        }
    }

    return false;
}

function parse( $html, $tags = array( ) )
{
    $maxProfundidad = 0;
    $trozos = array( $html );

    $pos_ini_etiq_fin = 0;
    $lon_cad_fin = strlen( $trozos[ $maxProfundidad ] );


    //Los cierres de las etiquetas siempre estarán en $cad_fin
    while ( $pos_ini_etiq_fin < $lon_cad_fin && ( $pos_ini_etiq_fin = strpos( $trozos[ $maxProfundidad ],
    '</', $pos_ini_etiq_fin ) ) !== false ) {
        // echo 'Encontrado posible cierre  at '.$pos_ini_etiq_fin.'<br />'."\r\n";

        if ( ( $pos_fin_etiq_fin = strpos( $trozos[ $maxProfundidad ], '>',
                $pos_ini_etiq_fin + 2 ) ) !== false ) {

            $etiqueta = rtrim( substr( $trozos[ $maxProfundidad ],
                            $pos_ini_etiq_fin + 2,
                            $pos_fin_etiq_fin - $pos_ini_etiq_fin - 2 ) );

            if ( in_array( $etiqueta, $tags ) ) {

                // echo 'Encontrada cierre '.$etiqueta.' at '.$pos_ini_etiq_fin.'<br />'."\r\n";

                $longitud = strlen( $etiqueta );

                //busco en el ultimo trozo
                $cord = findStartTag( $etiqueta, $trozos[ $maxProfundidad ],
                        $pos_ini_etiq_fin ); //: esto solo en la ultima profundidad

                if ( $cord !== false ) {

                    $trozos[ $maxProfundidad + 2 ] = substr( $trozos[ $maxProfundidad ],
                            $pos_fin_etiq_fin + 1 );

                    $trozos[ $maxProfundidad + 1 ] =
                            '<' . $etiqueta . ' ' . parseAndEraseJavascript( substr( $trozos[ $maxProfundidad ],
                                            $cord[ 0 ] + $longitud + 1,
                                            $cord[ 1 ] - $cord[ 0 ] - $longitud - 1 ) ) . '>' .
                            parseAmpersAndSelfClosed( substr( $trozos[ $maxProfundidad ],
                                            $cord[ 1 ] + 1,
                                            $pos_ini_etiq_fin - $cord[ 1 ] - 1 ),
                                    $tags ) .
                            '</' . $etiqueta . '>';

                    $trozos[ $maxProfundidad ] = substr( $trozos[ $maxProfundidad ],
                            0, $cord[ 0 ] );

                    $maxProfundidad += 2;
                    $lon_cad_fin = strlen( $trozos[ $maxProfundidad ] );
                    $pos_ini_etiq_fin = 0;
                } else {

                    $profundidad = $maxProfundidad - 2;

                    while ( $cord == false && $profundidad > -1 ) {
                        //ahora o busco la etiqueta de inicio en maxProfundidad o en maxProfundidad -2

                        $cord = findStartTag( $etiqueta, $trozos[ $profundidad ] ); //, $pos_ini_etiq_fin: esto solo en la ultima profundidad
                        //   echo $trozos[ $profundidad ].'<br />'."\r\n";

                        if ( $cord !== false ) {
                            //esto genera más trozos
                            //entre $maxProfundidad y $profundidad tengo que empaquetar los trozos

                            $trozos_new = array( );

                            for ( $i = 0; $i < $profundidad; $i++ )
                                    $trozos_new[ $i ] = $trozos[ $i ];

                            $trozos_new[ $profundidad ] = substr( $trozos[ $profundidad ],
                                    0, $cord[ 0 ] );

                            $interior = '';
                            $parseado = true;
                            for ( $i = $profundidad + 1; $i < $maxProfundidad;
                                        $i++ ) {
                                if ( $parseado ) $interior .= $trozos[ $i ];
                                else
                                        $interior .= parseAmpersAndSelfClosed( $trozos[ $i ],
                                            $tags );

                                $parseado = !$parseado;
                            }

                            $trozos_new[ $profundidad + 1 ] =
                                    '<' . $etiqueta . ' ' . parseAndEraseJavascript( substr( $trozos[ $profundidad ],
                                                    $cord[ 0 ] + $longitud + 1,
                                                    $cord[ 1 ] - $cord[ 0 ] - $longitud - 1 ) ) . '>' .
                                    parseAmpersAndSelfClosed( substr( $trozos[ $profundidad ],
                                                    $cord[ 1 ] + 1 ), $tags ) .
                                    $interior .
                                    parseAmpersAndSelfClosed( substr( $trozos[ $maxProfundidad ],
                                                    0, $pos_ini_etiq_fin ),
                                            $tags ) .
                                    '</' . $etiqueta . '>';

                            $trozos_new [ $profundidad + 2 ] = substr( $trozos[ $maxProfundidad ],
                                    $pos_fin_etiq_fin + 1 );

                            $trozos = $trozos_new;
                            unset( $trozos_new, $interior );

                            $maxProfundidad = $profundidad + 2;
                            $lon_cad_fin = strlen( $trozos[ $maxProfundidad ] );
                            $pos_ini_etiq_fin = 0;
                        }
                        else $profundidad -= 2;
                    }
                }

                // var_dump($trozos);
            }
        }
    }

    $interior = '';
    $parseado = false;
    for ( $i = 0; $i < $maxProfundidad + 1; $i++ ) {
        if ( $parseado ) $interior .= $trozos[ $i ];
        else $interior .= parseAmpersAndSelfClosed( $trozos[ $i ], $tags );

        $parseado = !$parseado;
    }

    return $interior;
}

$string = "" .
        "<<  << <das> <das a='hola' b = \"a asdas\" c=\" asdas \" /> <asdasd /> </das> &sada;  asdasd <<< <das /> <das> <das> jaijai </das> </das> />sadas";
echo $string . "\n";
echo parse( $string, array( 'das' ) );
?>
